﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Diagnostics;
using System.Text.RegularExpressions;
using System.Collections.Specialized;
using System.Threading.Tasks;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.Diagnostics;
using Microsoft.WindowsAzure.ServiceRuntime;
using Microsoft.WindowsAzure.StorageClient;
using System.Configuration;
using System.Threading;
namespace WorkerRole
{
    public static class MacrosManager
    {
        internal const string MACROS_STRING_PATTERN_CONFIG = "MacrosStringPattern";
        internal const string MACROS_FILE_NAMES_CONFIG = "MacrosReplacementFilePaths";
        private const string PROV_HIVE_CONFIG = "ProvisioningHive";
        private const string PKG_HIVE_CONFIG = "PackageHive";
        private const string DATA_CONNECTION_STRING_CONFIG = "DataConnectionString";
        private const string MACROS_BLOB_NAME = "ddmacrosvalues.txt";
        public static string AppRoot = "";
        public static string RoleRoot = "";
        public static string SitesRoot = "";
        public static readonly RetryPolicy RETRY_POLICY = RetryPolicies.Retry(3, TimeSpan.Zero);
        internal const int TIMEINSECSFORINFINITEINITWAIT = 60;
        private static string MACROS_PATTERN = "{[^}]*}";

        private static void FillEnvironmentVariables()
        {
            if (RoleEnvironment.IsAvailable)
            {
                try
                {
                    RoleRoot = Environment.GetEnvironmentVariable("RoleRoot");

                    AppRoot = Environment.GetEnvironmentVariable("RoleRoot") + @"\approot";
                    SitesRoot = Environment.GetEnvironmentVariable("RoleRoot") + @"\sitesroot";
                    if (!Directory.Exists(SitesRoot))
                    {
                        SitesRoot = null;
                        // May be WorkerRole
                        //AppRoot = Environment.GetEnvironmentVariable("RoleRoot") + @"\approot";
                        //SitesRoot = AppRoot;
                        if (RoleEnvironment.IsEmulated)
                        {

                            SitesRoot = AppRoot;
                        }
                    }

                    Trace.TraceInformation("Sites Root " + ((!string.IsNullOrEmpty(SitesRoot)) ? SitesRoot : "Seems to be a Worker Role"));
                    Trace.TraceInformation("AppRoot " + AppRoot);


                }
                catch (Exception ex)
                {
                    Trace.TraceError("Error retrieving environment variables and config variables in FillEnvironmentVariables() " + ex.Message);

                }
            }
        }

       
        private static bool GetFileListFromConfigurationAndReplaceMacros(StringDictionary macrosValues, string fileNames)
        {
            bool isComplete = true;

            try
            {
               
                string mp = GetConfigValueAsString(MACROS_STRING_PATTERN_CONFIG);
                if (!string.IsNullOrEmpty(mp))
                {
                    MACROS_PATTERN = mp;

                }
                string[] fileNamesArr = fileNames.Split(';', ',');

                foreach (string filePath in fileNamesArr)
                {

                    if (!File.Exists(filePath))
                    {
                        //Replace macros in the file
                        Trace.TraceError("File does not exist yet " + filePath);
                        return false;
                    }


                }

                //Once all the files are in place. Replce macros

                Parallel.ForEach<string>(fileNamesArr, filePath =>
                {

                    if (File.Exists(filePath))
                    {
                        string fileData = File.ReadAllText(filePath);


                        File.WriteAllText(filePath, ReplaceMacros(MACROS_PATTERN, fileData, macrosValues));

                        Trace.TraceInformation("Replaced macros in file " + filePath);
                      
                    }
                    else
                    {
                        //Replace macros in the file
                        Trace.TraceError("File does not exist yet " + filePath);
                     

                    }


                 


                });


            }
            catch (Exception ex)
            {

                Trace.TraceError("Error loading file list from configuration " + ex.Message);
                isComplete = false;
            }

            return isComplete;
        }


        private static string ReplaceMacros(string macrosMatchPattern, string inputConfigString, StringDictionary userInput)
        {

            //1. First get all the unique macros in the source
            Match[] matches = RegExUtils.FindSubstrings(inputConfigString, macrosMatchPattern, true);

            //TODO: Later on compare if the MACROS match the ones that are in the repository.

            //2. Get the Replacement Value for each match

            string source = inputConfigString;



            foreach (Match match in matches)
            {
                //get the match
                string matchValue = match.Value;
                string replacementValue = matchValue;
                //get the user input for the match

                try
                {
                    replacementValue = userInput[matchValue];

                }
                catch (Exception ex)
                {

                    throw new ArgumentException(string.Format("Value for {0} not provided. Exception: {1}", matchValue, ex.Message));
                }


                source = RegExUtils.Replace(source, matchValue, replacementValue, RegexOptions.Multiline);

            }//foreach

            return source;
        }

        private static StringDictionary CaptureAllMacros()
        {
            StringDictionary macrosNV = new StringDictionary();
            //Get provisioning hive
            string provHive = GetConfigValueAsString(PROV_HIVE_CONFIG);

            //In test environment $ddprovhive$ will remain as macros. This is filled in DD Deployment
            if (provHive != "$ddprovhive$")
            {
                string dataConnectionString = GetConfigValueAsString(DATA_CONNECTION_STRING_CONFIG);

                //Get macros from blob
                var blobReference = CloudStorageAccount.Parse(GetConfigValueAsString(DATA_CONNECTION_STRING_CONFIG)).CreateCloudBlobClient().GetContainerReference(provHive).GetBlobReference(MACROS_BLOB_NAME);


                blobReference.DownloadToFile(MACROS_BLOB_NAME, new BlobRequestOptions() { RetryPolicy = RETRY_POLICY });
                if (File.Exists(MACROS_BLOB_NAME))
                {
                    string fileContents = File.ReadAllText(MACROS_BLOB_NAME);

                    if (!string.IsNullOrEmpty(fileContents))
                    {

                        string[] macrosPairs = fileContents.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);

                        foreach (string nvpair in macrosPairs)
                        {
                            if (!string.IsNullOrEmpty(nvpair))
                            {
                                string[] macroArr = nvpair.Split(new char[] { '=' }, 2);

                                macrosNV.Add(macroArr[0], macroArr[1]);

                            }

                        }

                    }

                }
            }


            //Get macros from local machine (machine name, ipaddress, etc.)
            macrosNV.Add(LocalMacros.APP_ROOT, AppRoot);
            macrosNV.Add(LocalMacros.ROLE_ROOT, RoleRoot);
            macrosNV.Add(LocalMacros.SITES_ROOT, SitesRoot);
            macrosNV.Add(LocalMacros.DEPLOYMENT_ID, RoleEnvironment.DeploymentId);
            macrosNV.Add(LocalMacros.MACHINE_NAME, Environment.MachineName);
            macrosNV.Add(LocalMacros.PACKAGE_HIVE, GetConfigValueAsString(PKG_HIVE_CONFIG));
            macrosNV.Add(LocalMacros.PROVISIONING_HIVE, GetConfigValueAsString(PROV_HIVE_CONFIG));
            macrosNV.Add(LocalMacros.ROLE_INSTANCE_ID, RoleEnvironment.CurrentRoleInstance.Id);

            int i = 0;
            foreach (var endpoint in RoleEnvironment.CurrentRoleInstance.InstanceEndpoints)
            {
                i++;
                string variable = endpoint.Key;

                string port = endpoint.Value.IPEndpoint.Port.ToString();

                string ipAddress = endpoint.Value.IPEndpoint.Address.ToString();
                if (!macrosNV.ContainsKey(ipAddress))
                {
                    macrosNV.Add("{ipaddress_" +i + "}", ipAddress);
                }

                macrosNV.Add("{" + variable + ".ipaddress}", string.Format("{0}", ipAddress));
                macrosNV.Add("{" + variable + ".port}", string.Format("{0}", port));
            }



            return macrosNV;

        }

        public static void RunMacrosEngine(string fileNames)
        {



            try
            {
                FillEnvironmentVariables();

                StringDictionary macros = CaptureAllMacros();
              

                if (!string.IsNullOrEmpty(fileNames))
                {
                    if (!GetFileListFromConfigurationAndReplaceMacros(macros, fileNames))
                    {
                        Trace.TraceError("There was an error modifying the file macros. This can be disastrous. Please check your application logs.");
                     

                    }

                }
                else
                {

                    Trace.TraceInformation("No files to replace macros.");
                }

            }
            catch (Exception ex)
            {

                Trace.TraceError("Error modifying the file macros. Quitting..." + ex.Message);

                // Environment.Exit(-1);
            }

        }

     

        public static string GetConfigValueAsString(string configName)
        {
            return (RoleEnvironment.IsAvailable)
                                   ? RoleEnvironment.GetConfigurationSettingValue(configName)
                                   : ConfigurationManager.AppSettings[configName];

        }
    }
}
